#include <stdio.h>
#include "sonic.h"
#include "object.h"
#include "objectsystemNEWER.h"
#include "sprite.h"
#include "collision.h"

#include "objects/hud.h"
#include "objects/rings.h"
#include "objects/points.h"
#include "objects/monitor.h"
#include "objects/explosions.h"
#include "objects/animals.h"
#include "objects/springs.h"
#include "objects/spikes.h"
#include "objects/titlecard.h"
#include "objects/gotthroughcard.h"
#include "objects/signpost.h"
#include "objects/platforms.h"
#include "objects/scenery.h"
#include "objects/GHZ/bridge.h"
#include "objects/GHZ/motobug.h"
#include "objects/GHZ/chopper.h"
#include "objects/GHZ/buzzbomber.h"
#include "objects/GHZ/missile.h"
#include "objects/GHZ/newtron.h"
#include "objects/GHZ/crabmeat.h"
#include "objects/GHZ/purplerock.h"
#include "objects/GHZ/edgewalls.h"
#include "objects/GHZ/ledge.h"
#include "objects/GHZ/smash.h"
void (*objectMethodIndex[0xFF])(object*);


#define player objects[0]
extern object *objects;

extern byte *v_objstate; 

void pcObjectMethods()
{
     objectMethodIndex[1-1]=Sonic_Player;
     objectMethodIndex[0x0D-1]=Signpost;
     objectMethodIndex[0x11-1]=Bridge;
     objectMethodIndex[0x18-1]=BasicPlatform;
     objectMethodIndex[0x1A-1]=CollapseLedge;
     objectMethodIndex[0x1C-1]=Scenery;
     objectMethodIndex[0x1F-1]=Crabmeat;
     objectMethodIndex[0x21-1]=HUD;
     objectMethodIndex[0x22-1]=BuzzBomber;
     objectMethodIndex[0x23-1]=Missile;
     objectMethodIndex[0x25-1]=Rings;
     objectMethodIndex[0x26-1]=Monitor;
     objectMethodIndex[0x27-1]=ExplosionItem;
     objectMethodIndex[0x28-1]=Animals;
     objectMethodIndex[0x29-1]=Points;
     objectMethodIndex[0x2B-1]=Chopper;
     objectMethodIndex[0x34-1]=TitleCard;
     objectMethodIndex[0x36-1]=Spikes;
     objectMethodIndex[0x37-1]=RingLoss;
     objectMethodIndex[0x3A-1]=GotThroughCard;
     objectMethodIndex[0x3B-1]=PurpleRock;
     objectMethodIndex[0x3C-1]=SmashWall;
     objectMethodIndex[0x40-1]=MotoBug;
     objectMethodIndex[0x41-1]=Springs;
     objectMethodIndex[0x42-1]=Newtron;
     objectMethodIndex[0x44-1]=EdgeWalls;
     
}

void ExecuteObjects()
{
	
	int objProcCount = 127;
	if (objects[0].Routine == 6) { objProcCount = 31; }
	for (int obj = 0; obj < objProcCount; obj++) //for each object in v_objspace list
	{
		int objNum = objects[obj].ID;   //get object being referred to at current pos in list
		if (objNum != 0)   				//if this is a valid objcet
		{
                        if (objNum == 1 || objNum == 0x0D || objNum == 0x1F || objNum == 0x22 || objNum == 0x1C || objNum == 0x3A  || objNum == 0x3C || objNum == 0x42 || objNum == 0x23 || objNum == 0x18 || objNum == 0x2B || objNum == 0x21 || objNum == 0x25 || objNum == 0x26 || objNum == 0x27 || objNum == 0x28 || objNum == 0x29 || objNum == 0x34 || objNum == 0x11  || objNum == 0x1A || objNum == 0x40 || objNum == 0x41  || objNum == 0x44 || objNum == 0x36 || objNum == 0x37 || objNum == 0x3B)
                        {
			//objNum *= sizeof(long);		
			//funcptr temp=((ObjIndex-4)+objNum;   //TODO: SET THIS UP TO CALL CORRECT FUNCTION IN OBJ_INDEX
			(*objectMethodIndex[objNum-1])(&objects[obj]);
			//objNum = 0;
                        }
                        else
                        {
                            DeleteObject(&objects[obj]);
                            //printf("object number: %i\n", objNum);
                        }
		}
	}
	/*if (objects[0].Routine == 6)
	{
	        objProcCount = 95;
		for (int obj = 32; obj < objProcCount+32; obj++)
		{
			if (objects[obj].exists())
			{
				if (objects[obj].Render <= 0)
					DisplaySprite(&objects[obj]);
			}
                }
	}*/

}

void DeleteObject(object *obj)
{
    //; ---------------------------------------------------------------------------
    //; Subroutine to	delete an object
    //; ---------------------------------------------------------------------------

    //; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

    //DeleteObject:
    //movea.l	a0,a1		; move object RAM address to (a1)

    //DeleteChild:				; child objects are already in (a1)
    //moveq	#0,d1
    //moveq	#$F,d0
    int objectSize = 68; //64??

    //DelObj_Loop:
    //move.l	d1,(a1)+	; clear	the object RAM
    //dbf	d0,DelObj_Loop	; repeat for length of object RAM
    for (int byteNum = 0;objectSize>0; objectSize--, byteNum++)
        obj->bytes[byteNum] = 0;

    //rts	

}   //; End of function DeleteObject



void SpeedToPos(object *obj)
{

    //; ---------------------------------------------------------------------------
    //; Subroutine translating object	speed to update	object position
    //; ---------------------------------------------------------------------------

    //; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

    //SpeedToPos:
    //move.l	obX(a0),d2
    long obX = obj->Xdec;      //grab fixed decimal pt. version of X & 

    //move.l	obY(a0),d3
    long obY = obj->Ydec;      //Y positions

    //move.w	obVelX(a0),d0	; load horizontal speed
    signed short obVelX = obj->VelocityX;

    //ext.l	d0
    //asl.l	#8,d0		; multiply speed by $100
    signed long shiftX = obVelX * 0x100;  //a.k.a we convert velocity to gen's version of 'decimal' number to be added to X position 

    //add.l	d0,d2		; add to x-axis	position
    long newX = obX + shiftX;

    //move.w	obVelY(a0),d0	; load vertical	speed
    signed short obVelY = obj->VelocityY;
   

    //ext.l	d0
    //asl.l	#8,d0		; multiply by $100
    signed long shiftY = obVelY * 0x100; 

    //add.l	d0,d3		; add to y-axis	position
    long newY = obY + shiftY;

    //move.l	d2,obX(a0)	; update x-axis	position
    obj->Xdec = newX;         //we are updating the fixed pt. versions
    
    //move.l	d3,obY(a0)	; update y-axis	position
    obj->Ydec = newY;

    //rts	
}    // End of function SpeedToPos

void ObjectFall(object *obj)      //similar to speedtopos
{
//; ---------------------------------------------------------------------------
//; Subroutine to	make an	object fall downwards, increasingly fast
//; ---------------------------------------------------------------------------

//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


    //ObjectFall:
    //move.l	obX(a0),d2
    long obX = obj->Xdec;

    //move.l	obY(a0),d3
    long obY = obj->Ydec;

    //move.w	obVelX(a0),d0
    signed short obVelX = obj->VelocityX;

    //ext.l	d0
    //asl.l	#8,d0
    signed long shiftX = obVelX << 8;  //a.k.a we convert velocity to gen's version of 'decimal' number to be added to X position

    //add.l	d0,d2
    long newX = obX + shiftX;

    //move.w	obVelY(a0),d0
    signed short oldVelY = obj->VelocityY;

    //addi.w	#$38,obVelY(a0)	; increase vertical speed
    obj->VelocityY = obj->VelocityY + 0x38;

    //ext.l	d0
    //asl.l	#8,d0
    signed long shiftY = oldVelY << 8;
	
    //add.l	d0,d3
    long newY = obY + shiftY;

    //move.l	d2,obX(a0)	; update x-axis	position
    obj->Xdec = newX;         //we are updating the fixed pt. versions
    
    //move.l	d3,obY(a0)	; update y-axis	position
    obj->Ydec = newY;
  
    //rts	

} //; End of function ObjectFall
extern signed short v_anglebuffer;

signed short ObjFloorDist(object *obj)
{
    //ObjFloorDist:
    //move.w	obX(a0),d3
    word obX = obj->X;

    return ObjFloorDist(obj, obX);
}

signed short ObjFloorDist(object *obj, word obX)
{
    //ObjFloorDist2:
    //move.w	obY(a0),d2
    word obY = obj->Y;
	
    //moveq	#0,d0
    //move.b	obHeight(a0),d0
    //ext.w	d0
    word obHeight = obj->Height;

    //add.w	d0,d2
    word obBottom = obY + obHeight;

    //lea	(v_anglebuffer).w,a4
    signed short *v_anglebufferPtr = &v_anglebuffer;

    //move.b	#0,(a4)
    (*v_anglebufferPtr) = 0;

     //movea.w	#$10,a3
    signed short offset = 16;     // height of a 16x16 tile

    //move.w	#0,d6
    word collisionFlags = 0;

    //moveq	#$D,d5
    byte solidnessBit = 0xD;      // bit to test for solidness


    signed short floorDist;
    //bsr.w	FindFloor
    FindFloor(obj, obBottom, obX, solidnessBit, collisionFlags, floorDist, offset, v_anglebufferPtr);

    //move.b	(v_anglebuffer).w,d3
    byte angle = (v_anglebuffer);
    
    //btst	#0,d3
    //beq.s	locret_14E4E
    if ((angle & 1) == 1)
    {
        //move.b	#0,d3
        angle = 0;
    }
 
    //locret_14E4E:
    //rts	
    return floorDist;

}   //; End of function ObjFloorDist2

signed short ObjFloorDist(object *obj, byte &angle)
{
    word obX = obj->X;

    //ObjFloorDist2:
    //move.w	obY(a0),d2
    word obY = obj->Y;
	
    //moveq	#0,d0
    //move.b	obHeight(a0),d0
    //ext.w	d0
    word obHeight = obj->Height;

    //add.w	d0,d2
    word obBottom = obY + obHeight;

    //lea	(v_anglebuffer).w,a4
    signed short *v_anglebufferPtr = &v_anglebuffer;

    //move.b	#0,(a4)
    (*v_anglebufferPtr) = 0;

     //movea.w	#$10,a3
    signed short offset = 16;     // height of a 16x16 tile

    //move.w	#0,d6
    word collisionFlags = 0;

    //moveq	#$D,d5
    byte solidnessBit = 0xD;      // bit to test for solidness


    signed short floorDist;
    //bsr.w	FindFloor
    FindFloor(obj, obBottom, obX, solidnessBit, collisionFlags, floorDist, offset, v_anglebufferPtr);

    //move.b	(v_anglebuffer).w,d3
    angle = (v_anglebuffer);
    
    //btst	#0,d3
    //beq.s	locret_14E4E
    if ((angle & 1) == 1)
    {
        //move.b	#0,d3
        angle = 0;
    }
 
    //locret_14E4E:
    //rts	
    return floorDist;

}   //; End of function ObjFloorDist2


//; ---------------------------------------------------------------------------
//; Subroutine to	smash a	block (GHZ walls and MZ	blocks)
//; ---------------------------------------------------------------------------

//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

//SmashObject:
void SmashObject(object *obj, byte *fragData, long fragCount, word smashSpeed)
{
    //moveq	#0,d0
    //move.b	obFrame(a0),d0
    //add.w	d0,d0
    word mapOffset = obj->Frame * 2;

    //movea.l	obMap(a0),a3
    //adda.w	(a3,d0.w),a3
    byte *mapForFrame = obj->Map[mapOffset];

    //addq.w	#1,a3
    mapForFrame = mapForFrame + 1;     //skip mappings count

    #define currentBlock mapForFrame
    //from here on we change both the parent object and spawn additional objects to serve as children 
    //each child object/(fragment)'s mappings now points to a PORTION of the original object's mappings (a.k.a ONE block/five byte entry)
    
    //this means the RAW MAPPINGS flag has to be set, otherwise buildsprites will look for sub-mappings/multiple entrys when we only need a SINGLE entry
    //bset	#5,obRender(a0)
    obj->Render = obj->Render | 32;    

     //move.b	0(a0),d4
    byte parentID = obj->ID;

    //move.b	obRender(a0),d5
    byte parentRenderRaw = obj->Render;

    //movea.l	a0,a1
    object *childObj = obj;      //first child object will take place of the parent...

    //bra.s	@loadfrag
    //; ===========================================================================

    while (true)
    {
        //@loadfrag:
	//move.b	#4,obRoutine(a1)
        childObj->Routine = 4;

	//move.b	d4,0(a1)
        childObj->ID = parentID;

	//move.l	a3,obMap(a1)
        childObj->Map = (byte**)currentBlock;

	//move.b	d5,obRender(a1)
        childObj->Render = parentRenderRaw;

	//move.w	obX(a0),obX(a1)
        childObj->X = obj->X;

	//move.w	obY(a0),obY(a1)
        childObj->Y = obj->Y;

	//move.w	obGfx(a0),obGfx(a1)
        childObj->Gfx = obj->Gfx;
	
	//move.b	obPriority(a0),obPriority(a1)
        childObj->Priority = obj->Priority;
	
	//move.b	obActWid(a0),obActWid(a1)
        childObj->ActionWidth = obj->ActionWidth;
	
	//move.b	(a4)+,obVelX(a1)
        childObj->VelocityX = *(fragData++);

        //move.b	(a4)+,obVelY(a1)
        childObj->VelocityY = *(fragData++);

	//cmpa.l	a0,a1
	//bcc.s	loc_D268
        //bsr.w	DisplaySprite1
        if (childObj > obj) 
        { 

            //move.l	a0,-(sp)
	    //movea.l	a1,a0
	    //bsr.w	SpeedToPos
            SpeedToPos(childObj);

	    //add.w	d2,obVelY(a0)
            childObj->VelocityY = childObj->VelocityY + smashSpeed;

	    //movea.l	(sp)+,a0
	    //bsr.w	DisplaySprite1
            DisplaySprite(childObj); 
        }   
   
        //loc_D268:
	//dbf	d1,@loop
        if ((fragCount--) < 0) { break; }
         
        //loc_...:
	//bsr.w	FindFreeObj
        childObj = FindFreeObj();
 
	//bne.s	playsnd
        if (childObj->ID != 0) { /*playsound*/ break; }
 
	//addq.w	#5,a3
        currentBlock = currentBlock + 5;     //next mappings block
    }

    //@playsnd:
    //sfx	sfx_WallSmash,1 ; play smashing sound
}   //; End of function SmashObject
